#ifndef ASYNC_GENERATOR_H
#define ASYNC_GENERATOR_H

#include "ccoroutine_core_global.h"
#include "coroutine.h"

namespace CCoroutine{

template<typename T,typename ...Args>
class AsyncGenerator;

template<typename T,typename ...Args>
class AsyncGeneratorController:public ControllerBase
{
public:
    AsyncGeneratorController(){

    }

    ~AsyncGeneratorController(){
        if(base){
            delete base;
        }
    }

    AsyncGeneratorController(AsyncGeneratorController&& o):ControllerBase(std::move(o)){

    }

    AsyncGeneratorController& operator = (AsyncGeneratorController&& other){
        ControllerBase::operator=(std::move(other));
        return *this;
    }

    class NextAwaiter:public AwaiterBase{
    public:
        NextAwaiter(AsyncGenerator<T,Args...> *p):p(p){

        }
        bool ready(){

            if(!p)
            {
                return true;
            }

            if(p->finished)
            {
                return true;
            }

            return p->ready();
        }
        void suspend(PromiseBase* p){

            this->p->add_await(p);
        };
        void resume(){
        }
    private:
        AsyncGenerator<T,Args...> *p;
    };

    AwaiterBase* next(){
        auto p = dynamic_cast<AsyncGenerator<T,Args...>*>(base);
        p->m_ready = false;
        p->m_awaiting.clear();
        p->resume();
        return new NextAwaiter(p);
    }

    bool finished(){
        auto p = dynamic_cast<AsyncGenerator<T,Args...>*>(base);
        return p->finished;
    }

    T value(){
        return dynamic_cast<AsyncGenerator<T,Args...>*>(base)->yield_value();
    }
};

template<typename T,typename ...Args>
class AsyncGenerator:public ArgedPromise<Args...>{
public:
    typedef AsyncGeneratorController<T,Args...> Controller;
    AsyncGenerator(){

    }

    bool ready() const{
        return m_ready;
    }

    AwaiterBase* initial_suspend(){
        return new SuspendAlways{};
    }

    AwaiterBase* final_suspend(){
        finished =  true;
        return new SuspendAlways();
    }

    void add_await(PromiseBase* p){
        m_awaiting.push_back(p);
    }

    T yield_value(){
        return m_yield_value;
    }

    class YieldSuspend:public AwaiterBase{
    public:
        YieldSuspend( std::vector<PromiseBase*> m_awaiting):m_awaiting(m_awaiting)
        {

        }

        bool ready(){return false;}
        void suspend(PromiseBase* p){
            for(const auto &i:m_awaiting)
            {
                i->resume();
            }

        };
        void resume(){}

    private:
         std::vector<PromiseBase*> m_awaiting;
    };

    AwaiterBase* on_yield(T value){
        m_yield_value = value;
        m_ready = true;
        return new YieldSuspend(m_awaiting);
    }

private:
    bool m_ready = false;
    T m_yield_value = {};
    bool finished = false;
    std::vector<PromiseBase*> m_awaiting;
    friend class AsyncGeneratorController<T,Args...>;
};
}

#endif
